module examples.STMExample where

import Control.concurrent.STM
import Control.Concurrent

type Account = TVar Int

newAccount :: Int -> IO Account
newAccount amount = atomically $ TVar.new amount

deposit :: Account -> Int -> STM ()
deposit account amount = do
        v <- account.read
        account.write (v + amount)

withdraw :: Account -> Int -> STM ()
withdraw account amount = do
        v <- account.read
        account.write (v - amount)

limitedWithdraw :: Account -> Int -> STM ()
limitedWithdraw account amount = do
        withdraw account amount
        bal <- account.read
        check (bal >= 0)

transfer :: Account -> Account -> Int -> STM ()
transfer from to amount = do
        limitedWithdraw from amount
        deposit to amount

main = do
    a1 <- newAccount 100
    a2 <- newAccount  50
    a3 <- newAccount   0

    (v1, v2, v3) <- atomically $ do
        v1 <- a1.read
        v2 <- a2.read
        v3 <- a3.read
        return (v1, v2, v3)

    println (v1, v2, v3)

    let t12 = transfer a1 a2 70
        t23 = transfer a2 a3 35
        t31 = transfer a3 a1 70

    forkIO $ mapM_ atomically $ replicate 100 $ t12
    forkIO $ mapM_ atomically $ replicate 300 $ t23 `orElse` t31

    Thread.sleep 5000

    (v1, v2, v3) <- atomically $ do
        v1 <- a1.read
        v2 <- a2.read
        v3 <- a3.read
        return (v1, v2, v3)

    println (v1, v2, v3)

